// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package cpu

const CacheLinePadSize = 256

var HWCap uint

// bitIsSet报告是否设置了索引处的位。位索引
// 是大端顺序，因此位索引0是最左边的位。
func bitIsSet(bits []uint64, index uint) bool {
	return bits[index/64]&((1<<63)>>(index%64)) != 0
}

// 函数是命名函数的函数代码。
type function uint8

const (
	// KM{A，C，CTR}函数代码
	aes128 function = 18 // AES-128 
	aes192 function = 19 // AES-192 
	aes256 function = 20 // AES-256 

	// K{I，L}MD功能代码
	sha1     function = 1  // SHA-1 
	sha256   function = 2  // SHA-256 
	sha512   function = 3  // SHA-512 
	sha3_224 function = 32 // SHA3-224 
	sha3_256 function = 33 // SHA3-256 
	sha3_384 function = 34 // SHA3-384 
	sha3_512 function = 35 // SHA3-512 
	shake128 function = 36 // SHAKE-128 
	shake256 function = 37 // SHAKE-256 

	// KLMD功能代码
	ghash function = 65 // /GHASH 
)

const (
	// KDSA功能代码
	ecdsaVerifyP256    function = 1  // NIST P256 
	ecdsaVerifyP384    function = 2  // NIST P384 
	ecdsaVerifyP521    function = 3  // NIST P521 
	ecdsaSignP256      function = 9  // NIST P256 
	ecdsaSignP384      function = 10 // NIST P384 
	ecdsaSignP521      function = 11 // NIST P521 
	eddsaVerifyEd25519 function = 32 // Curve2519 
	eddsaVerifyEd448   function = 36 // Curve448 
	eddsaSignEd25519   function = 40 // Curve2519 
)

// queryResult包含查询函数
// 调用的结果。位按大端顺序编号，因此
// 最左边的位（MSB）位于索引0处。
type queryResult struct {
	bits [2]uint64
}

// 报告给定函数是否存在。
func (q *queryResult) Has(fns ...function) bool {
	if len(fns) == 0 {
		panic("no function codes provided")
	}
	for _, f := range fns {
		if !bitIsSet(q.bits[:], uint(f)) {
			return false
		}
	}
	return true
}

// facility是指定设施的位索引。
type facility uint8

const (
	// 强制设施
	zarch  facility = 1  // z体系结构模式为活动
	stflef facility = 7  // 存储设施列表扩展
	ldisp  facility = 18 // 长位移
	eimm   facility = 21 // 扩展立即

	// 杂项设施
	dfp    facility = 42 // 十进制浮点
	etf3eh facility = 30 // 扩展翻译3增强

	// 加密工具
	msa  facility = 17  // 消息安全辅助
	msa3 facility = 76  // 消息安全辅助扩展3 
	msa4 facility = 77  // 消息安全辅助扩展4 
	msa5 facility = 57  // 消息安全辅助扩展5 
	msa8 facility = 146 // 消息安全辅助扩展8 
	msa9 facility = 155 // 消息安全辅助扩展9 

	// 向量设施
	vxe facility = 135 // 向量增强1 

	// 注意：vx需要内核支持
	// 因此必须从HWCAP获取。

	hwcap_VX = 1 << 11 // 向量设施
)

// 设施列表包含STFLE调用的结果。
// 位按大端顺序编号，因此最左边的位（MSB）位于索引0处。
type facilityList struct {
	bits [4]uint64
}

// 报告给定的设施是否存在。
func (s *facilityList) Has(fs ...facility) bool {
	if len(fs) == 0 {
		panic("no facility bits provided")
	}
	for _, f := range fs {
		if !bitIsSet(s.bits[:], uint(f)) {
			return false
		}
	}
	return true
}

// 在cpu_s390x.s中定义了以下特征检测功能。
// 调用它们的代价可能很高，因此应该缓存结果。
func stfle() facilityList
func kmQuery() queryResult
func kmcQuery() queryResult
func kmctrQuery() queryResult
func kmaQuery() queryResult
func kimdQuery() queryResult
func klmdQuery() queryResult
func kdsaQuery() queryResult

func doinit() {
	options = []option{
		{Name: "zarch", Feature: &S390X.HasZARCH},
		{Name: "stfle", Feature: &S390X.HasSTFLE},
		{Name: "ldisp", Feature: &S390X.HasLDISP},
		{Name: "msa", Feature: &S390X.HasMSA},
		{Name: "eimm", Feature: &S390X.HasEIMM},
		{Name: "dfp", Feature: &S390X.HasDFP},
		{Name: "etf3eh", Feature: &S390X.HasETF3EH},
		{Name: "vx", Feature: &S390X.HasVX},
		{Name: "vxe", Feature: &S390X.HasVXE},
		{Name: "kdsa", Feature: &S390X.HasKDSA},
	}

	aes := []function{aes128, aes192, aes256}
	facilities := stfle()

	S390X.HasZARCH = facilities.Has(zarch)
	S390X.HasSTFLE = facilities.Has(stflef)
	S390X.HasLDISP = facilities.Has(ldisp)
	S390X.HasEIMM = facilities.Has(eimm)
	S390X.HasDFP = facilities.Has(dfp)
	S390X.HasETF3EH = facilities.Has(etf3eh)
	S390X.HasMSA = facilities.Has(msa)

	if S390X.HasMSA {
		// 密码消息
		km, kmc := kmQuery(), kmcQuery()
		S390X.HasAES = km.Has(aes...)
		S390X.HasAESCBC = kmc.Has(aes...)
		if facilities.Has(msa4) {
			kmctr := kmctrQuery()
			S390X.HasAESCTR = kmctr.Has(aes...)
		}
		if facilities.Has(msa8) {
			kma := kmaQuery()
			S390X.HasAESGCM = kma.Has(aes...)
		}

		// 计算消息摘要
		kimd := kimdQuery() // 中间（无填充）
		klmd := klmdQuery() // 最后（填充）
		S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
		S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
		S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
		S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH不存在
		sha3 := []function{
			sha3_224, sha3_256, sha3_384, sha3_512,
			shake128, shake256,
		}
		S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...)
		S390X.HasKDSA = facilities.Has(msa9) // 椭圆曲线
		if S390X.HasKDSA {
			kdsa := kdsaQuery()
			S390X.HasECDSA = kdsa.Has(ecdsaVerifyP256, ecdsaSignP256, ecdsaVerifyP384, ecdsaSignP384, ecdsaVerifyP521, ecdsaSignP521)
			S390X.HasEDDSA = kdsa.Has(eddsaVerifyEd25519, eddsaSignEd25519, eddsaVerifyEd448, eddsaSignEd448)
		}
	}

	S390X.HasVX = isSet(HWCap, hwcap_VX)

	if S390X.HasVX {
		S390X.HasVXE = facilities.Has(vxe)
	}
}

func isSet(hwc uint, value uint) bool {
	return hwc&value != 0
}
